home *** CD-ROM | disk | FTP | other *** search
/ Games of Daze / Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso / x2ftp / msdos / pmode / 386p_099 / tech.doc < prev    next >
Encoding:
Text File  |  1994-05-03  |  37.4 KB  |  864 lines

  1. 386POWER PROGRAMMER GUIDELINES
  2.  
  3. SMILEY WARNING:
  4.   I use "smileys" (the faces drawn with punctuation marks and other chars
  5.   you must roll your head to the left to see clearly)
  6.   a lot because i like to write as i talk
  7.   and i heavily use pronunciation inflection.
  8.   (read: the things i say, if they are written as they are,sometimes look like
  9.    an offence or a menace, the smiley correct this).
  10.    ;-) or ;)  == it's a joke, you know i don't mean exactly this.
  11.    :-) or :)  == happy
  12.    :-} or :}  == oops!
  13.    :-(        == i'm not happy of this
  14.  
  15. PG-A386 WARNING:
  16.   386POWER HAS BEEN RATED Programmer Grade - Assembly 386 !!!!! ;-)
  17.   I suppose you know enough of assembly and 386 programming.
  18.   If you don't, bad luck.
  19.   
  20. PMODE WARNING:
  21.   Since 386Power is sort of PMODE stepbrother i based this
  22.   sort of tech manual on the pmode.doc file included into pmode.zip
  23.   by Thomas "Tran" Pytel, this way it will be easier to get the differences
  24.   between the two extenders.
  25.   You might say i mention Tran too many times (or too few times
  26.   if you are Tran ;) ), fact is that i never read a book about 
  27.   386 protected mode programming, i just looked into tran docs and played 
  28.   with pmode, then i found the DPMI docs 
  29.   and the Ralph Brown Interrupt List on the internet
  30.   and decided to try to build 386Power.
  31.   Anyway, Tran docs together with the pmode sources were the most complete
  32.   thing i found about protected mode programming with some things
  33.   you hardly find in the docs (like how to set the pages under  VCPI).
  34.   If you are familiar with pmode, read carefully, there are some things
  35.   that are similar but a lot different.
  36.   
  37. ------------------------------------------------------------------------------
  38. Who did it:
  39.  
  40. 386POWER (386P for short ) has been conceived and coded 
  41. by Lorenzo Micheletto (that's me) but is not all "my stuff" (besides mostly is).
  42. It is based on the PMODE386 dos-extender by Tran (a.k.a. Thomas Pytel). 
  43. I used modified algorithms found in PMODE 386 and in some situations
  44. i copied some chunks of code (i.e. the interrupt redirectors).
  45. I'm not the guy who reinvent the wheel every time (just NEARLY every time ;) ).
  46.  
  47. And now a little fairy tale about rampaging mechs ....
  48. Lorenzo Micheletto (that's me), once an 8086 assembly programmer
  49. was building a game engine under MS-Dos in the free time to build
  50. a super beat 'em up game based on rampaging mechs.
  51. ( but sooner or later you will see MechaFighter IV hit the 'net
  52.   after i make a 'test' game to see what's the limit of 386P )
  53. After realizing 640k couldn't handle all the graph already produced....
  54. ( immagine how much memory is needed to store all the sprites and 
  55.   composed-sprite data for the 150ton  MCT-2S MechTerminator 
  56.   when it fires at close range a 500mm ShotCan 
  57.   [ binary barrel short range cannon, sort of grown-up shotgun ]
  58.   charged with Depleted-Uranium swarm-bullets )
  59. ... i tried PMODE (the release with RAW,XMS,VCPI and DPMI support).
  60.  
  61. But after i discovered some quirks (gasp! my test code crashed
  62. in strange ways and i was closely following what Tran wrote in the docs)
  63. i first reversed engineered all PMODE to find out what was going wrong
  64. then digged in the VCPI and DPMI docs, then changed things here and ...
  65. ... restarted from zero. 
  66.  
  67. I was shaking the system upside-down a lot,so if something was going wrong, it 
  68. was hard to me understand if was my fault, pmode fault or an interaction bug.
  69.  
  70. Well, after two months of ripping up PMODE i gave up
  71. ( i couldn't read Tran mind, so some things looked like a bug to me
  72.   but could be just arcane programming, not to mention i HATE code that
  73.   self- modifies because it easily triggers heuristic antiviral routines)
  74. and decided to build a similar thing (i already had translated lots of
  75. code from real-mode-286 to protected-mode-386-with-pmode386, so the
  76. PMODE API was familiar to me, why change ? )
  77. that i could understand and fix without problems.
  78.  
  79. Starting from zero source lines i ended up with this strange thing:
  80. 386POWER, a dos extender interface a lot similar to PMODE
  81. (nearly the same API, and some portions of it
  82. are copied straight from pmode) with only VCPI and DPMI support.
  83.  
  84. GASP! I started patching a program and ended up with another one!
  85. (sort of genetic programming technique ;-) )
  86.  
  87. 386Power checks if a VCPI or DPMI manager is present
  88. (read: it needs EMM386, QEMM or Windows running in 386 Enhanced Mode)
  89. and if it finds one it initialize the protected mode environment
  90. for 32bit FLAT protected mode.
  91. IRQs are active and redirected as expected.
  92. ALL interrupt vectors gets copied to the _OldInt table
  93. (and restored on program termination) so you don't need to save them.
  94.  
  95. ------------------------------------------------------------------------------
  96. First of all, the memory layout, as you can guess is nearly the same of PMODE:
  97.  
  98. Your program will  have at least 3 segments
  99.  
  100. code16   A 16bit segment that holds all the real mode and 16bit protected
  101.          mode init and exit code.
  102.          It has to be the first segment of the EXE.
  103.          
  104. code32   The huge 32bit segment. You can throw in as much code as will fit
  105.          in low memory (no 64k fixup overflows). If you need more code
  106.          space, you're gonna have to load it into extended memory at runtime
  107.          and use it there. 
  108.          Under protected mode, addresses (code and data, they're the
  109.          same memory space) are offset from the beginning of this segment.
  110.          I'll explain later how to access things outside this segment.
  111.          
  112. codeend  This MUST be the last segment in the EXE. It is the base for the
  113.          stack and low memory allocation.
  114.          If you want to add other segments insert them between
  115.          code32 and codeend
  116.  
  117. two things seems to miss ... 
  118. 1) The STACK...
  119. The stack is shared by your protected mode program and real mode calls. 
  120. This means it can be at most 64k wide (don't worry it's wide enough).
  121. The stack region always begins at codeend, and goes on for STACKSIZE paragraphs.
  122. (a value declared in 386power.inc), but the stack 
  123. you can use under a certain context (prot. or real mode) can be at most 
  124. STACKSLOT paragraphs (more on this later).
  125.  
  126. 2) The HEAP....
  127.   There are TWO HEAPS you can allocate memory from at run time. 
  128.   
  129. The "Low memory" heap, which covers all conventional memory below A0000h. 
  130. The "Extended memory" heap, which covers all those nice bytes above 1M. 
  131.  
  132. There's a reason for keeping them separate (other than
  133. that big hole between A0000h and 100000h (which I did not want to fill in or
  134. rearrange with paging) : DOS can only see the low memory heap. 
  135.  
  136. So in calls where you have to pass buffer addresses, you must pass only buffers 
  137. that you have in low memory. 
  138. Low memory is the place where you would allocate any critical disk or
  139. TWIN DMA buffers (more on this later).
  140.  
  141. ------------------------------------------------------------------------------
  142. VIRTUAL REGISTERS
  143.   You call real mode interrupts and far routines with "virtual registers".
  144. Memory images of the registers as they will be set for the real mode int.
  145. When the mode-switch occours the current values are pushed into the stack
  146. the mode switch occours & the new register values are loaded from
  147. the virtual registers (actually just a table of dwords used by int 32h
  148. and int 33h mode switch routines)
  149.  
  150. ------------------------------------------------------------------------------
  151. Details of runtime:
  152.  
  153. After initialization of the protected mode environment
  154. 386POWER will call a label called _Main located in the code32 segment
  155. (so place _Main: where you want to start your code)
  156.  
  157. When _Main is reached you can assume that:
  158. 1) The stack is set up in the first slot (more on this later).
  159. 2) The interrupts are disabled (and have been all the way from real mode) just
  160.    in case theres something you want to do first.
  161. 3) CS points to the code segment you're running in (code32)
  162.    thru a selector set up by 386POWER.
  163. 4) DS,ES,FS, and SS point to an alias of the code segment (same memory). (In
  164.   case you don't remember, you can NEVER write to a CS: override in protected
  165.   mode, only read or execute are allowed in a protectded mode code segment).
  166.   
  167. N.B. Both selectors in 3) and 4) are set with a 4Gigabytes limit
  168.      soyou can stuff anything you want into them.
  169. 5) GS is a segment that's 4Gigabyte wide but starts at absolute 0. 
  170.    This is of course useful for accessing the real mode dos data area, or 
  171.    the BIOS data area, or the PSP, etc...
  172. 6) The TYPEMATIC RATE is set to minimal values
  173.    (this is very useful if you are gonna perform "raw keyboard" input)
  174. 7) ALL interrupt vectors are copied to the _OldInt table
  175.    (they will be restored on interrupt termination)
  176.    so you can change the dos interrupt vectors without having to be worried
  177.    to save 'em.
  178. 8) If you run under DPMI, 386P tries to lock all memory allocated
  179.    but doesn't care if it has been successful or not
  180.    (i do this just to reduce disk swapping to the minimum).
  181.    If you need locked memory (i.e. for IRQ handling) use the DPMI functions
  182.    if you detect DPMI (check the _386Man var.).   
  183.  
  184.  
  185. Selectors:
  186.  
  187.   There are three main selectors you have to know. 
  188. _SelCode, _SelData, and _SelZero are 16bit word vars you can access to get 
  189. the selector values for the code, data, and zero (GS) segments respectively. 
  190. When 386power pass control to _main you can assume
  191. CS=_SelCode, DS=ES=FS=SS=_seldata, and GS=_selzero. 
  192. You can change the segregs if you wish (for example to do a REP MOVS in the zero seg). 
  193. But the 386POWER routines and ints expect the segregs to be these values 
  194. (except for the special case of SS into an ISR). 
  195. And these MUST be the values when you jump to '_Exit' to return to DOS. 
  196. Another thing that is assumed by 386POWER is DF=0 
  197. (direction flag is clear (like the CLD instruction)).
  198. You can perform STD, but before calling 386POWER stuff you have to
  199. "re-set" the direction flag with CLD.
  200.  
  201. Linear & relative addresses:
  202.  
  203. All addresses of thing declared in code32 are 
  204. RELATIVE to the beginning of the code32 segment 
  205. (which could start anywhere in low memory). 
  206. nearli all the system code expects addresses that are code32 relative.
  207.  
  208. For this reason, you must adjust any physical memory pointers 
  209. before you use them. 
  210. That is, to access something at linear address A0000h 
  211. (A000:0000, in 16 bit seg:ofs notation) you can use GS 
  212. (it is loaded with the _SelZero selector) and write GS:0A0000h.
  213.  
  214. If you want to write to A0000h using the default data segment 
  215. it will not be at DS:A0000, but at 
  216.         DS:(A0000h - linear_address_of_the_beginning_of_Code32). 
  217. And this linear_address_of_the_beginning_of_Code32
  218. is stored in a variable called '_Code32Base'. 
  219. So if the segment code32 is 1F43, the linear address will be 1F430h 
  220.  
  221. To get a pointer to A0000h, you would do something like:
  222.  
  223.   mov eax,0A0000h
  224.   sub eax,_Code32Base
  225.  
  226. This function is provided as a macro in 386power.inc
  227. (copied straight from pmode together with the protected mode ISR
  228.  handlers :} )
  229. together with the macro to go the other way, relative address to linear. 
  230. Of course, if you address something with GS (assuming GS is _SelZero)
  231. you can use the actual linear address. 
  232. The linear address for code16 is also provided in _Code16Base.
  233. As well as the linear address of the PSP in '_PSPBase'. 
  234. The linear addresses of code16 and PSP will always be less than code32.
  235.  
  236. To access them (memory pointed to by them, these vars are in code32), you will
  237. have to use one of two methods. One is easy enough, just use the GS segment.
  238. Or you could use negative indexes from the normal segment, causing a
  239. 4Gbyte wraparound (a lot like the 64kbyte wraparound under real mode).
  240. However it is rare to access things outside code32, so my advice is to use GS
  241. to make things clear and as a remainder of things "done outside code32".
  242.  
  243.  
  244. Memory where is my memory?
  245.  
  246.   As for the memory. You have two heaps, into low and high (extended) memory.
  247. Each of which is guaranteed to be at least as much as you specified in LOWMIN
  248. and EXTMIN in 386power.inc, the startup code will grab all low memory for you 
  249. (because it's meant to run standalone), and it will attempt 
  250. to grab all the high memory it can. 
  251. Two dword vars hold information about each memory area. _LoMemBase and
  252. _LoMemTop specify the base and top of the low memory pool as relative
  253. addresses (ready to use, no adjustment needed). 
  254. The total amount of low memory available in bytes is _LoMemTop - _LoMemBase 
  255. (notice _LoMemTop points to one byte beyond the last available byte). 
  256. The _GetLoMem routine is a very simple routine that takes a length in EAX
  257. and checks to see if there is enough low memory. 
  258. If there is enough, it adds the length to _LoMemBase and
  259. returns a pointer (code32 relative, ready to use) in EAX 
  260. to the low memory block along with the carry flag clear. 
  261. If it finds not enough memory, it returns with the carry flag set. 
  262. _HiMemBase, _HiMemTop, and _GetHiMem are the same thing for high memory.
  263. Then there is the _GetMem routine that first tries to allocate
  264. low memory and if this fails it tries high memory.
  265.  
  266.  
  267. Calling real (actually quite virtual) mode:
  268.  
  269. You can call real mode, and back. This is only provided so that 
  270. you can call real mode interrupts, and routines that
  271. you can't recode in protected mode. 
  272. Keep these cross-mode calls to the minimum because they eat lots of cpu time
  273. and can raise some nasty interaction bugs.
  274. You can call real mode interrupts or procedures from protected mode 
  275. through INT 32h (call real mode far proc), and INT 33h (call real mode int). 
  276.  
  277. These interrupts are only available to the PROTECTED MODE part of your program. 
  278.  
  279. In REAL MODE, there is a ANOTHER INT 32h that you can use 
  280. to call a protected mode routine. 
  281. Don't confuse the two INT32s with each other, though they do basically 
  282. the same thing BUT THEY ARE NOT THE SAME THING AND THEY DO DIFFERENT THINGS. 
  283. The "int 32h from real mode" doesn't use virtual registers, no register
  284. parameter passing is possible it is available 
  285. just to implement sort of callback system.
  286.  
  287. To pass register values  from protected mode to real mode  and back 
  288. you use 'virtual registers'.
  289. These 'virtual registers' are merely memory images of EAX,EBX,ECX,EDX,ESI,EDI,
  290. EBP,DS,ES,FS,and GS. AL and AH and AX and BL ... etc ... are there too, and
  291. they share the appropriate memory space with each other so if you change the
  292. 'virtual' AH register, the 'virtual' AX and EAX registers will be changed
  293. accordingly. 
  294. There are no SS,ESP,CS,EIP registers. CS:EIP is taken from the real mode int
  295. vector table for int calls, and passed in the real CX:DX registers for a procedure call. 
  296. SS:ESP is set up by 386POWER together with the extended memory manager
  297. providing protected mode services.
  298.  
  299. @ INT 33h from prot. mode: Do a real mode interrupt.
  300.   AL=interrupt you want to do. All V86??? general and segment registers will
  301.   be passed to the real mode handler. They will also be passed back as the
  302.   return values into the virtual register. 
  303.   The carry, zero, aux, parity, sign, and overflow flags will
  304.   be passed back as the actual CPU flags. The real mode interrupt will be
  305.   called with interrupts disabled (as it is usually). Keep in mind, no CPU
  306.   registers will be modified (except the flags mentioned). Only their V86???
  307.   images will be changed by the real mode int handler.
  308.  
  309. @ INT 32h from prot. mode: Call a real mode far procedure.
  310.   CX:DX=seg:off you want to call. The register passing works just like INT33.
  311.   Except that the interrupt flag will be preserved across the call to real
  312.   mode (but not back, the IF flag will be in the same state as it was before
  313.   the int).
  314.  
  315. @ INT 32h from real mode: Call a pmode procedure.
  316.   EDX=off. A 32bit offset in the code32 segment. The register passing works
  317.   just like for the other INT32 in pmode, except that 
  318.   register (real or virtual they are) ARE NOT PASSED TO OR FROM
  319.   the prot. mode routine. Upon entry to the routine, the system standard
  320.   things are set. That is:
  321.     CS=_SelCode, DS=ES=FS=SS=_SelData, GS=_SelZero, DF=0 (CLD).
  322.   And they must be that when the thing executes its RET (not RETF).
  323.   But registers and flags won't be passed as happens 
  324.   on the int32 from prot. mode. (the real mode int32 is left as a 
  325.   quick way to implement callbacks and just these)
  326.  
  327.  
  328. DPMI and IRQs:
  329.  
  330.   Upon startup, all the interrupt vectors for IRQs point to routines that
  331. redirect the IRQs to their default real mode handlers. You can hook into any
  332. IRQ you want. There are two dword pointers that allow you to get and set IRQ
  333. vektorz. _GetIRQ and _SetIRQ point to routines to get and set the
  334. relative address of the handler for specific IRQs within the code32 segment. 
  335. To get the address of a handler, just do a 'call _GetIRQ' with BL set to the
  336. IRQ num you want (0-15). EDX will be returned pointing to its current handler.
  337. To set an IRQ, pass BL again as the IRQ number, and EDX as the offset of the
  338. new handler. You can chain to the old handler if you want just by jumping to
  339. the old address when your handler is done processing.
  340.  
  341.   When your IRQ handler is called, you can be sure of ONLY TWO THINGS. The IF
  342. flag is clear and CS is loaded with _SelCode. 
  343. All the general regs and segregs should be treated as undefined. 
  344. Even SS cannot be trust, because under DPMI it is set to a little 
  345. "interrupt stack" set up by DPMI. 
  346. In other words ... DON'T CHANGE STACK!!!!
  347.  
  348.   Another consideration for DPMI is the IF flag. According to DPMI specs, only
  349. CLI, STI, and INT 31h functions AX=900h and AX=901h should be counted on to
  350. modify the interrupt flag (POPF(D) and IRET(D) should not). This is because
  351. certain DPMI systems might have to virtualize the interrupt flag, and keep the
  352. real flag enabled at all times (but don't worry, if the 'virtual' flag is
  353. clear, your program will not get any IRQs). In practice, certain DPMIs do
  354. allow IRET(D)s and POPF(D)s to modify the virtual interrupt flag. But this is
  355. inconsistent across them. So you should follow these rules:
  356.  
  357. @ CLI and STI are allowed, and do their functions.
  358. @ Don't assume anything about POPF(D) and IRET(D) and the interrupt flag.
  359. @ Don't assume the interrupt flag PUSHF(D) stores on the stack is correct,
  360.   it might be the real flag or the 'virtual' flag.
  361. @ These DPMI INT 31h functions are supported under VCPI too
  362.   (by way of the 386POWER interface).
  363.   ) AX=900h: Get state of IF and disable it. Returns AL set to the IF flag.
  364.   ) AX=901h: Get state of IF and enable it. Returns AL set to the IF flag.
  365.   ) AX=902h: Only returns AL set to the IF flag (0=disabled, 1=enabled).
  366. @ At the end of an IRQ handler, put a STI. When the handler is called, flags
  367.   are automatically disabled. 
  368.   If you do not reenable them, and neither does the IRETD... 
  369.   Well... under some systems it will run and on other it will hang.
  370.  
  371.  
  372. The stack:
  373.  
  374. 386POWER uses one major stack for both pmode and real mode. This stack is
  375. always located in low memory (always locked under DPMI). 
  376. The size of the stack is set as STACKSIZE in 386power.inc . 
  377. There is another value there called STACKSLOT, 
  378. this is what must be explained.
  379.  
  380.   When a mode switch occurs, the new stack is the old stack base minus
  381. STACKSLOT paragraphs. The stack base is the stack location when your program
  382. starts. And it is only modified by mode switches.
  383. That is the stack uses STACKSIZE paragraphs  and it is "sliced"
  384. into slots STACKSLOT wide.
  385. Your main program uses just one stack "slot" and when a mode switch occours
  386. another "slot" is used as stack, then returning from the mode switch
  387. the stack becomes the old "slot".
  388. Into each slot you can safely allocate local variables on the stack
  389. referencing them with esp ad ebp without having to worry of mode switches
  390. or reflected irqs triggering a mode switch.
  391. Every time you switch mode a new slot is used and when you "get back"
  392. to the caller, the caller "slot" becomes the stack.
  393.  
  394. So if STACKSIZE = 4000 and STACKSLOT=1000
  395. your main program cau use up to 1000 bytes
  396. If a mode switch occours, a news stack is set up and it is wide
  397. 1000 bytes too.
  398.  
  399. Let's make an example:
  400.  
  401. Immagine the following "chunks of code"
  402. A is the main program
  403. B is a real mode routine
  404. C is a real mode timer driven interrupt service routine 
  405. D is a protected mode routine
  406.  
  407. To describe the stack status we will use the following notation:
  408.  
  409. XYZ
  410. 123---  a stack structure with six slots and slots 1,2,3 "in use"
  411.         where 3 is the "active stack" and is in use by the Z subroutine
  412.        
  413. 1] A is running
  414.  
  415. A
  416. 1-----
  417.  
  418. 2] A calls B (mode switch)
  419.  
  420. AB
  421. 12----
  422.  
  423. 3] B calls D (mode switch)
  424.  
  425. ABD
  426. 123---
  427.  
  428. 4] C triggered by timer interrupt (unexpected mode switch you cannot control)
  429.  
  430. ABDC
  431. 1234--
  432.  
  433. 5] interrupt handler returns
  434.  
  435. ABD
  436. 123---
  437.  
  438. 6] D returns
  439.  
  440. AB
  441. 12----
  442.  
  443. 7] B returns
  444.  
  445. A
  446. 1-----
  447.  
  448. As you can guess, you'd better keep enough slots available because
  449. if too many mode switch "accumulates" the stack structure will break.
  450. Follow this rule of the thumb:
  451. slots = maximum_number_of_cross_mode_switches +
  452.         maximum_number_of_irq_can_nest_between_mode_switches
  453.         
  454. The best way to minimize IRQ problems is the use of TWIN IRQ handlers
  455. (two different IRQ ISR routines, one coded for real mode, the other coded
  456.  for protected mode, this way you are sure no mode switches
  457.  will happen because of interrupts triggered by not-deterministic events).
  458. If you really have to switch mode, keep the Interrupt flag disabled
  459. to prevent "unexpected irq stackings". 
  460.  
  461.  
  462. Under DPMI things are slightly different but the rule
  463. "the less mode switches, the better" is still true. 
  464. DPMI handles stack switching on its own, any IRQ causes a switch 
  465. to a totally different stack provided by DPMI (argh!), this is the main
  466. reason i had to stick with the "slotted stack" model as in pmode
  467. and keep a var to point to the "next slot" to support int32h/33h
  468. in protected mode from within interrupts.
  469. Under real mode irqs and interrupts, if you want to call the "old"
  470. inteerupt handler, instead of having to store the address somewhere
  471. every time, just call the equivalent entry stored into the _OldInt table
  472. (it's the same table used by INT 33h from protected mode).
  473.  
  474. To summarize:
  475. @ In an IRQ handler, DON'T switch off the stack it is entered with. Which is
  476.   not guaranteed that SS=_seldata.
  477. @ Don't do more nested calls across modes than (STACKSIZE/STACKSLOT)-1. 
  478.   (-2 be totally safe).
  479. @ You CAN safely assume SS=_seldata in protected mode only in your main stream
  480.   of execution, and in routines that are called with INT32 from real mode.
  481. @ Consider your maximum effective stack size to be STACKSLOT.
  482. @ You CAN call across modes using INT32/33 from an IRQ handler in both real
  483.   and protected mode, but remember this takes time
  484.   (giving IRQs enough time to stockpile if they are fast enough ).
  485. @ Under real mode IRQ, if you want to call directly the "old" interrupt
  486.   just perform the following actions:
  487.         pushf
  488.         call dword ptr cs:[offset _OldInt + INT_NUM*4]  
  489.   
  490. Exceptions:
  491.  
  492. Are handled entirely by the DPMI host in those systems. 
  493. In VCPI, exceptions 0-5 and 7 are reflected to real mode 
  494. just like IRQs would be.
  495. Exceptions 8 and 9-1fh cause immediate termination
  496. (maybe in future releases i will include some debug messages when
  497.  terminating due to an exception). 
  498.  
  499. ------------------------------------------------------------------------------
  500. Potential DMA problems:
  501.  
  502.   As you know, the DMA controllers in the PC use all physical addresses.
  503. Nothing but the processor itself knows how linear memory is arranged in the
  504. physical memory banks. When paging is disabled, the relationship is very
  505. simple. The linear address is always the same as the physical address. But
  506. when you enable paging, that could get all screwed up. 
  507. Under VCPI and DPMI paging IS enabled. (bad luck, uh?)
  508.  
  509. You can almost definately count on extended memory addresses 
  510. not being consistent with their physical addresses. 
  511. Low memory however, will usually map perfectly to its physical addresses. 
  512. Usually i said, NOT always.
  513.  
  514. The point is that you shouldn't use "raw" DMA under VCPI and DPMI.
  515.  
  516. To handle DMA you can try to make tricky things or you can follow the
  517. Virtual DMA Specification (VDS). This is the recommended way of handling DMA 
  518. under VCPI and DPMI. See the Ralph Brown interrupt list for INT 4Bh 
  519. for more info.
  520.  
  521. A future release of 386P will include VDS support thru standard routines
  522. as soon as i find the best way to handle some quirks.
  523.  
  524. ------------------------------------------------------------------------------
  525. And now to discuss some of the finer points of different protected mode
  526. environments:
  527.  
  528. VCPI:
  529.  
  530.   VCPI is nearly "raw" mode. The CPL is 0, and there is nothing scrutinizing
  531. the execution of your code. Paging is enabled, but if you want 
  532. you can mess directly with page physical addressed.
  533. The problem comes with the way VCPI compatibility works. To call a real mode
  534. interrupt or procedure, we have to pass protected mode control back to the
  535. VCPI server. This comes out to one thing. IRQs that occur in a real mode call
  536. will NOT make it to your protected mode handler. It's just the way VCPI works. 
  537. Under VCPI the real mode interrupt table and the protected mode
  538. interrupt table belongs to different Task Segment Descriptors.
  539.  
  540. @ Do the IRQ handler in real mode. That way, it will always be called no
  541. matter what is in control. But this seems to defeat the purpose of protected
  542. mode. And if this is a timing critical IRQ, you have a problem because passing
  543. control from a program (prot. mode) to the VCPI server to execute the real mode
  544. IRQ callback takes a bit of time. Not a terrible amount, but it is a delay.
  545.  
  546. @ Do the IRQ handler in protected mode, and keep real mode calls to a minimum.
  547. For example, disable all but the critical IRQs to your program. And try to
  548. handle as many as you can in pmode. (You can read the keyboard direct from the
  549. hardware can't you. And you do know how to output FFh to A1h and FDh to 21h).
  550. But remember one thing... When you go to do a real mode call (DOS file call or
  551. something else you can't do yourself). Whatever the hardware cause of your IRQ
  552. will still be active. And if an IRQ occurs in real mode, and there is no real
  553. mode handler for it. Well, you know... So you either put in some valid real
  554. mode handler that may merely set a flag that you have an IRQ to service. Or
  555. disable the source of the IRQ (mask it off at 21h or A1h).
  556.  
  557. @ Use TWO irq handlers (one in real mode and one
  558.   in protected mode ) to handle the same data structure
  559.   to pass data to/from the main program.
  560.   It is harder to code but a lot faster that "switchy" stuff.
  561.  
  562. ------------------------------------------------------------------------------
  563. DPMI:
  564.  
  565.   DPMI is not that bad. It could be a little more consistent across 
  566. its implementations and let you select CPL0 for "trusted" applications. 
  567. I don't like the overhead imposed by  CPL3 
  568. (in CPL3, certain instructions have to be emulated by software). 
  569. Multitasking virtual machines in general are not that hot 
  570. when you're trying to do a timing critical action game. 
  571.  
  572. One really annoying problem with DPMI is that current implementations are
  573. far from perfect.
  574. QDPMI 1.01 for example, dies when an IRQ occurs in a prot. mode
  575. call from a real mode IRQ. 
  576. DPMI docs say this shouldn't happen, and it doesn't under Windows 3.1 DPMI 
  577. implementation. 
  578. The Windows 3.1 DPMI is a little better but has lethal quirks
  579. (try to make coexist paging and Virtual Dma Support if you don't believe me).
  580.  
  581. While testing the 386power alpha release the DPMI startup used to go bonkers 
  582. doing quite funny things, then i discovered that the "installation check"
  583. function wrongly reported DPMI 1.0 as the server while
  584. Windows 3.1 supports DPMI 0.9 ... 
  585. Gasp! My code was optimized to take advantage
  586. of the extra function provided by DPMI 1.0 if present and so ...
  587.  
  588.   Hmm, another little problem is that I'm not sure how many DPMIs out there
  589. actually reflect IRQs to real mode if they occur in protected mode. 
  590. Windows 3.1 seems to send them all over as it should. 
  591. According to Tran QDPMI 1.01 sends IRQ1, but not IRQ0.
  592. And it also doesn't seem to pass IRQs that occur in real mode
  593. through their protected mode handlers, while Windows 3.1 does.
  594.  
  595. If you code for VCPI, you must support interrupt reflection by yourself :-(.
  596.  
  597. Under DPMI, the dpmi server should reflect interrupts for you
  598. (as happens under Win 3.1) but if it doesn't ... you can't bypass it
  599. to reflect things yourself (damn!).
  600. I tried to do it, but strange things happened, my antivirus started
  601. yelling like a maniac about unbeliavable viral attacks 
  602. ( programs trying to become resident thru interrupt handling routines
  603.   and tunneling from CPL3 to CPL0 )
  604. and the dmpi server got nuked.
  605.  
  606. ------------------------------------------------------------------------------
  607. Some misc notes:
  608.  
  609. @ Under VCPI, 386Power will map as much extended memory as it can, up to
  610. 64M without allowing the page tables to use up more memory than would leave
  611. LOWMIN. Allocating up to 64M means that extended memory under VCPI 
  612. can be at most 63M (even if there is more available).
  613.  
  614. @ Before exiting your program, you do NOT need to restore any vectors
  615.   (protected or real mode) nor the typematic rate.
  616.   And you do not have to restore the IRQ masks at 21h and A1h (PMODE stores
  617.   them before jumping to _main, and restores them before exiting).
  618.   BUT if you reprogrammed the irq8/int70h timer interval, set it back to its
  619.   initial value 
  620.   (the irq0/int8 is automatically set back to 18.2 Hz by the _Exit routine).
  621.  
  622. @ If you're gonna add other 16bit segments, put them in between code32
  623.   and codeend.
  624.  
  625. @ Remember that upon reaching '_Main', interrupts are still disabled. Don't
  626.   forget to do the STI.
  627.  
  628.  
  629. INTERSEGMENT ACCESS:
  630.  Sometimes some variables declared in Code32 are accessed
  631.  from Code16 (eech!) as bad as it can be, it saved me lots of
  632.  segment swapping.
  633.  I learned this trick looking into PMODE386 and decided to use it
  634.  because even if it causes quite an headache to follow the code
  635.  it saves from the problems you run into when you swap segment register
  636.  and mode frequently, but remember the following rules:
  637.  a)
  638.  When you do this, it is better to use a
  639.  segment override to indicate what segment you are using.
  640.  i.e if in code16 you write DS:V86eax 
  641.      it means DS default segment ( == code16)
  642.      and offset of V86eax (located into code32)
  643.          CALCULATED FROM CODE16!!!!
  644.  So be aware of where you declare data and from where you reference it.
  645.  b) 
  646.  If you put too much stuff in code16 or code32 forcing the code 
  647.  accessed thru intersegment to use offsets beyound 64k you'll get a
  648.  "hidden" protection violation (some assemblers don't detect it!!!!!)
  649.  So if something strange happens, you know where to look at first.
  650.  
  651. if you look into 386P remember:
  652. d16_ means DPMI stuff in code16
  653. d32_ means DPMI stuff in code32
  654. v16_ means VCPI stuff in code16
  655. v32_ means VCPI stuff in code32
  656. s16_ means MIXED stuff in code16
  657. s32_ means MIXED stuff in code32
  658.  
  659. MIXED == shared between VCPI/DPMI xor "dos-exending" code
  660.  
  661. ------------------------------------------------------------------------------
  662. Heres a list of the vars provided by PMODE to your program:
  663.  
  664. _LoMemBase:dword
  665.   Low mem base for allocation (first free byte).
  666.  
  667. _LoMemTop:dword
  668.   Top of low mem (last free byte +1).
  669.  
  670. _HiMemBase:dword
  671.   High mem base for allocation (first free byte).
  672.  
  673. _HiMemTop:dword
  674.   Top of high mem (last free byte +1).
  675.  
  676. _PSPBase:dword
  677.   Absolute linear address of start of PSP.
  678.  
  679. _Code16Base:dword
  680.   Absolute linear address of start of code16.
  681.  
  682. _Code32Base:dword
  683.   Absolute linear address of start of code32 (32bit code offset from this).
  684.  
  685. _SelCode:word
  686.   Code segment selector.
  687.  
  688. _SelData:word
  689.   Data segment alias selector for code.
  690.  
  691. _SelZero:word
  692.   Data segment starting at absolute 0.
  693.  
  694. _386man:byte
  695.   386 manager flags, they have the following meanings 
  696.   (in the future they may also contain information about unified
  697.    DMA services, timer services and more).
  698.   bits:
  699.     0: 0=VCPI, 1=DPMI
  700.     1-7: undefined
  701.  
  702. _GetIRQ:dword
  703.   A pointer to the get IRQ function appropriate for the mode.
  704.   The function takes arguments as follows:
  705.     In:
  706.       BL  == IRQ num (0-0fh)
  707.     Out:
  708.       EDX == offset of current IRQ handler
  709.  
  710. _SetIRQ:dword
  711.   A pointer to the set IRQ function.
  712.     In:
  713.       BL  == IRQ num (0-0fh)
  714.       EDX == offset of new IRQ handler to set
  715.       
  716. _OldInt:table of 256 dwords LOCATED IN THE CODE16 SEGMENT
  717.         It contains all the original real mode int vectors found at
  718.         386P startup. Instead of storing "old" real mode interrupts yourself
  719.         386P does it for you. To chain to an old int just jump or pushf&call
  720.         to the equivalent entry in _OldInt.
  721.  
  722. ------------------------------------------------------------------------------
  723. And now some 'functions'. Remember, they ALL need:
  724.   CS=_SelCode, DS=ES=FS=_Seldata, GS=_SelZero, DF=0 (CLD).
  725.  
  726. _GetMem:
  727.   Allocate any mem, (first checks low, then high)
  728.   In:
  729.     EAX == size requested
  730.   Out:
  731.     CF=0  memory allocated
  732.     CF=1  not enough mem
  733.     EAX == relative pointer to mem or undefined if not enough
  734.  
  735. _GetLoMem:
  736.   Allocate some low mem
  737.   In:
  738.     EAX == size requested
  739.   Out:
  740.     CF=0  memory allocated
  741.     CF=1  not enough mem
  742.     EAX == relative pointer to mem or undefined if not enough
  743.  
  744. _GetHiMem:
  745.   Allocate some high mem
  746.   In:
  747.     EAX == size requested
  748.   Out:
  749.     CF=0  memory allocated
  750.     CF=1  not enough mem
  751.     EAX == linear pointer to mem or undefined if not enough
  752.  
  753. _GetIRQMask:
  754.   Get status of IRQ mask bit (at port 21h or A1h)
  755.   In:
  756.     BL == IRQ num (0-15)
  757.   Out:
  758.     AL == status: 0=enabled, 1=disabled
  759.  
  760. _SetIRQMask:
  761.   Set status of IRQ mask bit
  762.   In:
  763.     BL == IRQ num (0-15)
  764.     AL == status: 0=enabled, 1=disabled
  765.  
  766. _Exit:
  767.   Exit to real mode, restore initial IRQ mask
  768.   restore timer 0 frequency to 18.2 Hz
  769.   restore ALL interrupt vectors
  770.   and get out setting video mode 03 (color text 80x25)
  771.  
  772. ------------------------------------------------------------------------------
  773. PROGRAM SUPPORT
  774.  
  775. I'm not going to go commercial with 386Power and its companion code.
  776. Maybe i will go commercial with the games i will produce with it
  777. (i'm already working on a "Commander Keen like" game)
  778. Anyway i will distribuite the sources of this and ALL the future releases
  779. of 386power and its companion code (386video, and all the other mode-x stuff).
  780.  
  781. If 386Power doesn't work on your system i'd like to know (so i can fix it) 
  782. you can reach me at the addresses listed in the end.
  783. Call me if you have suggestions about new improvements and things like that.
  784.  
  785. BUT REMEMBER, 386power is NOT TOTALLY FREE (gosh!) 
  786.  
  787. If you use 386P "as is",you are NOT allowed to remove the messages 
  788. displayed by  the 386power startup code AND if you use it
  789. remember to include me and Tran (i owe it to him) in the list of who
  790. devenloped the software.
  791.  
  792. If you modify 386P (not just cosmetic adjustments of course)
  793. you should use a different name (to avoid confusion)
  794. BUT you have to include almost the following message
  795. "based on the 386Power dos-extender by Lorenzo Micheletto MCHLNZ67T19C890A
  796.  and on the PMODE dos-extender by Thomas 'Tran' Pytel".
  797.  
  798. What's more if you make a commercial thing with it .... 
  799. ... send me a free copy :).
  800.  
  801. I won't ask anything more from you.
  802. It is not a free lunch ... it is a nearly free lunch.
  803.  
  804. The main reason i coded 386P was for pure fun and get skilled about
  805. protected mode programming before coding the real game
  806. (if i just wanted a dos-extender i would have bought one from FlashTek).
  807. I distribuite it because: (in priority order)
  808. a) Needed a wider test base (just my 386 and the 486s' of some friend
  809.    is a too restricted test base)
  810. b) I owe it to all the people who distribuited their sources on the net
  811.    and let me learn how to handle the pc hardware and more.
  812. c) Need to get a reputation as a programmer, sooner or later
  813.    when i will try to publish/sell my games it could be useful.
  814.  
  815. ------------------------------------------------------------------------------
  816.  
  817. Final things:
  818.  
  819. 386Power has been designed with a restricted goal to reach
  820. (kick my code into 32bit protected mode :} and be sure that on program
  821. termination no bad things will happen).
  822. You are free to use it and change it any way you want, but please
  823. don't make protected mode viruses or trojan horse, i really hate those
  824. losers that think they are smart because they can destroy things.
  825. If you think you are smart, design and code a terrific piece of software.
  826.  
  827. And as i said before 386P is limited to VCPI and DPMI (if you call it a limit).
  828. If you want a "real" dos-extender capable to handle anything 
  829. contact the FlashTek guys and buy it 
  830. or search the latest pmode release by Tran (Thomas Pytel) on internet.
  831.  
  832. To reach FlashTek try to ask about them in the rec.games.programmer
  833. internet newsgroup.
  834.  
  835. To find Tran, do the same or ... 
  836. ....as far i know on may 1993 he could be reached on 
  837.     Creativity Demo Net or SBCnet as 'Tom Tran'
  838.     or at the Sound Barrier: (718)979-6629, (718)979-9406
  839.     or on Internet: tran@phantom.com
  840.  
  841.  
  842. If you want to contact me (for bug reports or other things)
  843. you can reach me ....
  844.  
  845. On the internet as:
  846. knight@arianna.dei.unipd.it
  847. or
  848. knight@maya.dei.unipd.it
  849. or
  850. knight@paola.dei.unipd.it
  851. (Yeah! Multiple logins! I'm moving from a server to another, so you'd
  852. better send to both )
  853.  
  854. By plain mail at the following address:
  855. Lorenzo Micheletto 
  856. Via Piazza Miega 10/A
  857. Veronella (VERONA)
  858. ITALY 37040
  859.  
  860. N.B. Maybe in 1995 i will be reachable only by plain mail
  861.      ( i will be in the army  for a year )
  862.      but after that i will be back to Padova.
  863.      
  864.